import { DataService } from '../services';
import { ListView, DetailView, View } from '../core';
import { ModelStore } from './ModelStore';
import { 
  StringPropertyEditor, 
  NumberPropertyEditor, 
  BooleanPropertyEditor, 
  DatePropertyEditor,
  DropDownPropertyEditor
} from '../components/PropertyEditors';
import { CommonViewController } from '../controllers/CommonViewController';

/**
 * ViewFactory - Creates view instances from model definitions
 */
export class ViewFactory {
  private static _instance: ViewFactory;
  private _modelStore: ModelStore;
  private _dataService: DataService;

  private constructor() {
    this._modelStore = ModelStore.getInstance();
    this._dataService = DataService.getInstance();
  }

  /**
   * Get the singleton instance of the ViewFactory
   */
  public static getInstance(): ViewFactory {
    if (!ViewFactory._instance) {
      ViewFactory._instance = new ViewFactory();
    }
    return ViewFactory._instance;
  }
  /**
   * Create a view from its name
   * @param viewName The name of the view to create
   */
  public createView(viewName: string): ListView | DetailView | null {
    console.log(`ViewFactory: Creating view for viewName=${viewName}`);
    
    // Get the view definition from the model store
    const viewDef = this._modelStore.getViewByName(viewName);
    console.log(`ViewFactory: View definition found:`, viewDef);
    
    if (!viewDef) {
      console.error(`View definition not found: ${viewName}`);
      return null;
    }

    // Get the business object definition
    const boName = viewDef.objectType;
    const boDefinition = this._modelStore.getBusinessObjectByName(boName);
    console.log(`ViewFactory: Business object definition for ${boName}:`, boDefinition);
    
    if (!boDefinition) {
      console.error(`Business object definition not found: ${boName}`);
      return null;
    }

    // Create appropriate view based on type
    if (viewDef.viewType === 'list') {
      console.log(`ViewFactory: Creating list view for ${viewName}`);
      return this.createListView(viewDef, boDefinition);
    } else if (viewDef.viewType === 'detail') {
      console.log(`ViewFactory: Creating detail view for ${viewName}`);
      return this.createDetailView(viewDef, boDefinition);
    }

    console.error(`Unsupported view type: ${viewDef.viewType}`);
    return null;
  }
  /**
   * Create a list view from a model definition
   * @param viewDef View definition
   * @param boDefinition Business object definition
   */
  private createListView(viewDef: any, boDefinition: any): ListView {
    // Create data source
    const dataSource = this._dataService.getDataSource(boDefinition.name);
    
    // Create list view
    const listView = new ListView(viewDef.name, viewDef.displayName, dataSource);
    
    // Set columns
    const columns = this.getColumnsForListView(viewDef, boDefinition);
    listView.setColumns(columns);
    
    // Add controllers
    this.setupControllersForView(listView, viewDef);
    
    return listView;
  }
  /**
   * Create a detail view from a model definition
   * @param viewDef View definition
   * @param boDefinition Business object definition
   */
  private createDetailView(viewDef: any, boDefinition: any): DetailView {
    // Create data source
    const dataSource = this._dataService.getDataSource(boDefinition.name);
    
    // Create detail view
    const detailView = new DetailView(viewDef.name, viewDef.displayName, dataSource);
    
    // Set up property editors
    this.setupPropertyEditorsForDetailView(detailView, viewDef, boDefinition);
    
    // Set up related views (nested views)
    this.setupRelatedViewsForDetailView(detailView, viewDef);
    
    // Add controllers
    this.setupControllersForView(detailView, viewDef);
    
    return detailView;
  }

  /**
   * Setup controllers for a view
   * @param view The view to setup controllers for
   * @param viewDef The view definition
   */
  private setupControllersForView(view: View, viewDef: any): void {
    console.log(`ViewFactory: Setting up controllers for view ${view.name}`);
    
    // Add CommonViewController to all views
    const commonController = new CommonViewController();
    view.addController(commonController);
    
    // Check for actions in view definition
    if (viewDef.actions && Array.isArray(viewDef.actions)) {
      console.log(`ViewFactory: View ${view.name} has defined actions:`, viewDef.actions);
    }
  }

  /**
   * Set up related views for a detail view
   * @param detailView Detail view to set up
   * @param viewDef View definition
   */
  private setupRelatedViewsForDetailView(detailView: DetailView, viewDef: any): void {
    // For each nested view in the view definition
    if (!viewDef.nestedViews) {
      return;
    }

    for (const nestedViewDef of viewDef.nestedViews) {
      // Create the related view using the view factory
      const relatedView = this.createView(nestedViewDef.viewName);
      if (relatedView) {
        // Add the related view to the detail view
        detailView.addRelatedView(nestedViewDef.association, relatedView);
      }
    }
  }

  /**
   * Get column definitions for a list view
   * @param viewDef View definition
   * @param boDefinition Business object definition
   */
  private getColumnsForListView(viewDef: any, boDefinition: any): any[] {
    const columns: any[] = [];
    
    // Map column names to properties
    for (const columnName of viewDef.columns) {
      const property = boDefinition.properties.find((p: any) => p.name === columnName);
      if (property) {
        columns.push({
          fieldName: property.name,
          caption: property.displayName,
          width: this.getDefaultWidthForType(property.type),
          dataType: property.type
        });
      }
    }
    
    return columns;
  }
  /**
   * Set up property editors for a detail view
   * @param detailView Detail view to set up
   * @param viewDef View definition
   * @param boDefinition Business object definition
   */
  private setupPropertyEditorsForDetailView(detailView: DetailView, viewDef: any, boDefinition: any): void {
    // For each group in the view definition
    for (const group of viewDef.groups || []) {
      // For each property in the group
      for (const propertyName of group.items || []) {
        const property = boDefinition.properties.find((p: any) => p.name === propertyName);
        if (property) {
          // Create the appropriate property editor based on type
          let editor;
          
          switch (property.type) {
            case 'string':
              editor = new StringPropertyEditor(property.name, property.displayName);
              break;
            case 'number':
              editor = new NumberPropertyEditor(property.name, property.displayName);
              break;
            case 'boolean':
              editor = new BooleanPropertyEditor(property.name, property.displayName);
              break;
            case 'datetime':
              editor = new DatePropertyEditor(property.name, property.displayName);
              break;
            default:
              // For lookups or other special types
              if (property.lookupDataSource) {
                editor = new DropDownPropertyEditor(property.name, property.displayName);
              } else {
                editor = new StringPropertyEditor(property.name, property.displayName);
              }
          }
          
          // Set editor properties
          if (property.isReadOnly) {
            editor.readOnly = true;
          }
          
          // Add the editor to the detail view
          detailView.addPropertyEditor(editor);        }
      }
    }
  }

  /**
   * Get default width for a property type
   * @param propertyType The type of the property
   */
  private getDefaultWidthForType(propertyType: string): number {
    switch (propertyType) {
      case 'boolean':
        return 80;
      case 'number':
        return 100;
      case 'datetime':
        return 150;
      case 'string':
        return 200;
      default:
        return 150;
    }
  }
}
